#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <iostream>
#include <boost/algorithm/string.hpp>
#include "TCPCommon.h"

//#define DEBUG

using namespace kc_sf;

TCPCommon::TCPCommon( int tcp_sockfd, unsigned int max_line_length )
: tcp_sockfd ( tcp_sockfd ),
  tcp_max_line_length ( max_line_length )
{
}

// ------------------------------------------------------------ //

void TCPCommon::setToNonBlocking()
{
	int flags = fcntl( tcp_sockfd, F_GETFL );
	flags |= O_NONBLOCK;
	fcntl( tcp_sockfd, F_SETFL, flags );
}

// ------------------------------------------------------------ //

CommResult TCPCommon::sendData( const std::string &what, int timeout_s )
{
	const char *buffer = what.data();
	long len = what.size();
    long done = 0, b_sent = 0;

    fd_set write_flag;
    while( done < len )
    {
        FD_ZERO( &write_flag );
        FD_SET( tcp_sockfd, &write_flag );
        struct timeval timeout;
        timeout.tv_sec=timeout_s;
        timeout.tv_usec=0;

        int res = select( tcp_sockfd + 1, 0, &write_flag, 0, &timeout );
        if( res == -1 )
        {
        	std::cerr << "select() failure" << std::endl;
            return Failure;
        }
        if( res == 0 )
        {
        	std::cerr << "select() timeout" << std::endl;
            return Timeout;
        }
        if( res == 1 )
        {
            b_sent = send( tcp_sockfd, buffer + done, len - done, 0 );

            if( b_sent <= 0 )
            	return Failure;

        	// Wyslano
			#ifdef DEBUG
				std::string sent = what.substr( done, len - done );
				std::cout << "OUT: " << buffer  << std::endl;
			#endif // DEBUG

            done += b_sent;
        }
    }

    return Success;
}

// ------------------------------------------------------------ //

CommResult TCPCommon::recvData(
		std::string &to,
		const std::string &until_str,
		int timeout_s )
{
	to.clear();

    unsigned long total_rcvd = 0;
    fd_set read_flag;

    while( true )
    {
        FD_ZERO( &read_flag );
        FD_SET( tcp_sockfd, &read_flag );
        struct timeval timeout;
        timeout.tv_sec=timeout_s;
        timeout.tv_usec=0;

        int res = select( tcp_sockfd + 1, &read_flag, 0, 0, &timeout );
        if( res == -1 )
        {
        	std::cerr << "select() failure" << std::endl;
            return Failure;
        }
        if( res == 0 )
        {
        	std::cerr << "select() timeout" << std::endl;
            return Timeout;
        }
        if( res == 1 )
        {
            const size_t Len = tcp_max_line_length + 1;
            char * buffer = new char[ Len ];

            int b_rcvd = recv( tcp_sockfd, buffer, Len, 0 );
			*( buffer + b_rcvd ) = '\0';

			// Otrzymano
#ifdef DEBUG
			std::cout << "IN: " << buffer << std::endl;
#endif // DEBUG

            if (b_rcvd > 0)
            {
				total_rcvd += b_rcvd;
				to += buffer;

				if( boost::algorithm::iends_with( to, until_str ) )
				{
				    delete [] buffer;
					return Success;
				}
            }

		    delete [] buffer;

            if( b_rcvd < 0 )
            	return Failure;
        }
    }

    return Success;
}

// ------------------------------------------------------------ //

CommResult TCPCommon::recvData(
		std::string &to,
		int timeout_s,
		unsigned long how_many_bytes )
{
	to.clear();
	if( how_many_bytes == ( size_t )( -1 ) )
		how_many_bytes = tcp_max_line_length;

    unsigned long total_rcvd = 0;
    fd_set read_flag;

    while( 1 )
    {
        FD_ZERO( &read_flag );
        FD_SET( tcp_sockfd, &read_flag );
        struct timeval timeout;
        timeout.tv_sec=timeout_s;
        timeout.tv_usec=0;

        int res = select( tcp_sockfd + 1, &read_flag, 0, 0, &timeout );
        if( res == -1 )
        {
        	std::cerr << "select() failure" << std::endl;
            return Failure;
        }
        if( res == 0 )
        {
        	std::cerr << "select() timeout" << std::endl;
            return Timeout;
        }
        if( res == 1 )
        {
            const size_t Len = tcp_max_line_length + 1;
            char * buffer = new char[ Len ];

            int b_rcvd = recv( tcp_sockfd, buffer, Len, 0 );

			// Otrzymano
#ifdef DEBUG
			std::cout << "IN: " << buffer << std::endl;
#endif // DEBUG

            if (b_rcvd > 0)
            {
				total_rcvd += b_rcvd;
				*( buffer + b_rcvd ) = '\0';
				to += buffer;

				if( total_rcvd >= how_many_bytes )
				{
				    delete [] buffer;
					return Success;
				}
            }

		    delete [] buffer;

            if( b_rcvd < 0 )
            	return Failure;
        }
    }

    return Success;
}
